﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CopyOnPremisesStep.cs" company="">
//   
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace ExecutionTool.CopyOnPremises
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Threading.Tasks;

    using ExecutionTool.Common;
    using ExecutionTool.ImportExport;
    using ExecutionTool.Profile;

    using Microsoft.Practices.EnterpriseLibrary.Logging;
    using Microsoft.WindowsAzure.StorageClient;

    public class CopyOnPremisesStep : StepBase<CopyOnPremisesStepInfo>
    {
        #region Fields

        private readonly ExportStepInfo exportStepInfo;

        private readonly IOnPremisesAdapter onPremisesAdapter;

        private readonly IStorageAdapter storageAdapter;

        private CopyOnPremisesStepInfo copyOnPremisesStepInfo;

        #endregion

        #region Constructors and Destructors

        public CopyOnPremisesStep(
            Profile profile, 
            ExportStepInfo exportStepInfo, 
            IStorageAdapter storageAdapter, 
            IOnPremisesAdapter onPremisesAdapter, 
            LogWriter logger = null)
            : base(profile, true, logger)
        {
            Helper.CheckForNull(exportStepInfo, "exportStepInfo");
            Helper.CheckForNull(storageAdapter, "storageAdapter");
            Helper.CheckForNull(onPremisesAdapter, "onPremisesAdapter");

            this.exportStepInfo = exportStepInfo;
            this.storageAdapter = storageAdapter;
            this.onPremisesAdapter = onPremisesAdapter;
            this.CanAbort = false;
        }

        #endregion

        #region Methods

        protected override void AbortAction()
        {
        }

        protected override void CleanupAction()
        {
        }

        protected override void OnFinished()
        {
            // set metadata
            var metadata = new Dictionary<string, string>
                {
                    { "DatabaseName", this.Profile.Source.DatabaseName }, 
                    { "Edition", this.exportStepInfo.DatabaseInfo.Edition.ToString() }, 
                    { "MaxSizeInGb", this.exportStepInfo.DatabaseInfo.MaxSizeInGb.ToString(CultureInfo.InvariantCulture) }
                };
            this.onPremisesAdapter.SetMetadata(this.BuildFilePath(), metadata);

            base.OnFinished();
        }

        protected override double ProgressHeartbeatAction()
        {
            var filePath = this.BuildFilePath();
            var size = this.onPremisesAdapter.GetFileSizeInBytes(filePath);
            var progress = size * 100 / this.exportStepInfo.BacpacFileSizeInBytes;
            return Math.Round(progress, 2);
        }

        protected override CopyOnPremisesStepInfo StartAction()
        {
            /*
             * make sure the target folder (container) exist;
             * make sure the blob name doesn't exist
             * 
             * open blob stream, open file stream
             * do while not end of stream
             *   read and write (buffer)
             */

            // Test connection will make sure the folder exist in the first place
            var folderPath = Path.Combine(this.Profile.TargetOnPremisesLocation.Location, this.exportStepInfo.ContainerName);
            Directory.CreateDirectory(folderPath);
            var filePath = this.BuildFilePath();

            if (File.Exists(filePath))
            {
                throw new StepFaultException(
                    "File with the same name exists in the target location.", 
                    StepType.CopyOnPremises, 
                    ErrorCodes.CopyingToOnPremisesFileAlreadyExistsError);
            }

            var blobRef = this.storageAdapter.GetBlobReference(this.Profile.TargetAzureStorage, this.exportStepInfo.BlobAddress);

            Task.Factory.StartNew(
                () =>
                    {
                        using (var targetStream = File.Open(filePath, FileMode.Create, FileAccess.ReadWrite))
                        {
                            var options = new BlobRequestOptions
                                {
                                   Timeout = new TimeSpan(1, 0, 0), RetryPolicy = RetryPolicies.Retry(20, TimeSpan.Zero) 
                                };
                            blobRef.DownloadToStream(targetStream, options);
                        }
                    }).ContinueWith(
                        t =>
                            {
                                if (t.IsFaulted)
                                {
                                    this.OnFaulted(t.Exception, FaultedLocation.SelfDetected, ErrorCodes.CopyingToOnPremisesDownloadError);
                                }
                            });

            this.copyOnPremisesStepInfo = new CopyOnPremisesStepInfo(filePath);
            return this.copyOnPremisesStepInfo;
        }

        private string BuildFilePath()
        {
            var filePath = Path.Combine(this.Profile.TargetOnPremisesLocation.Location, this.exportStepInfo.BlobAddress);
            return filePath;
        }

        #endregion
    }
}
